﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Noris.Schedule.Support.Services
{
	// DEKLARACE interface a args PRO SLUŽBY TYPU "KRESLÍŘ GRAFICKÝCH ELEMENTŮ"
	#region INTERFACE IGraphElementPainter : předpis pro Paintera elementů
	/// <summary>
	/// Interface, který předepisuje vlastnosti objektu, který chce být malířem elementů grafu.
	/// </summary>
	public interface IGraphElementPainter
	{
		/// <summary>
		/// Zde malíř elementu vyjadřuje svoji touhu kreslit konkrétní typ položky grafu (elementu).
		/// Na vstupu je uvedena specifikace elementu: typ grafu, číslo třídy elementu, typ elementu.
		/// Zdejší objekt vyhodnotí zadané údaje a rozhodne se, zda bude chtít daný typ elementu kreslit.
		/// Toto rozhodnutí platí pro všechny takto specifikované elementy.
		/// Pokud nechce malovat, vrátí 0.
		/// Pokud se sejde více tříd malířů, které by chtěly kreslit stejný element, pak vyhraje ta, která vrací vyšší číslo.
		/// Tato metoda se volá nejvýše jedenkrát za života jednoho grafu, vrácená hodnota platí pro všechny elementy tohoto grafu.
		/// </summary>
		/// <param name="elementKey">Specifikace elementu grafu</param>
		/// <returns>Hodnota vyjadřující moji touhu kreslit tento element</returns>
		float GetPriority(GraphElementPainterKey elementKey);
		/// <summary>
		/// Výkonná metoda kreslení elementu.
		/// Tady se painter může projevit.
		/// Anebo může nadefinovat sadu dat do argumentu, a jádro pak podle těchto dat vykreslí element.
		/// </summary>
		/// <param name="args"></param>
		void Paint(GraphElementPainterArgs args);
		/// <summary>
		/// PaintAfter() : je voláno po dokončení základního kreslení. Painter může něco málo nakreslit navrch přes defaultní kresbu.
		/// </summary>
		/// <param name="args"></param>
		void PaintAfter(GraphElementPainterArgs args);
	}
	#endregion
	#region CLASS GraphElementPainterKey : SPECIFIKACE ELEMENTU, KTERÝ MÁ BÝT KRESLEN
	/// <summary>
	/// Specifikace elementu, který se má kreslit. Rovněž hraje roli klíče v úložišti painterů pro jednotlivé elementy.
	/// </summary>
	public class GraphElementPainterKey
	{
		public GraphElementPainterKey(RowGraphMode graphMode, int classNumber, GraphElementLayerType layerType, GraphElementShapeType elementShapeType)
		{
			this._GraphMode = graphMode;
			this._ClassNumber = classNumber;
			this._LayerType = layerType;
			this._ElementShapeType = elementShapeType;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{GraphMode=" + GraphMode + "; ClassNumber=" + ClassNumber + "; DrawType=" + ElementShapeType + "}";
		}
		public override int GetHashCode()
		{
			return this._GraphModeInt ^ this._ClassNumber ^ this._LayerTypeInt ^ this._ElementShapeTypeInt;
		}
		public override bool Equals(object obj)
		{
			if (obj is GraphElementPainterKey)
			{
				GraphElementPainterKey other = (GraphElementPainterKey)obj;
				return
					(this._GraphModeInt == other._GraphModeInt &&
					 this._ClassNumber == other._ClassNumber &&
					 this._LayerType == other._LayerType &&
					 this._ElementShapeTypeInt == other._ElementShapeTypeInt);
			}
			return false;
		}
		/// <summary>
		/// Typ grafu v aktuálním řádku
		/// </summary>
		public RowGraphMode GraphMode { get { return this._GraphMode; } }
		/// <summary>
		/// Číslo třídy elementu
		/// </summary>
		public int ClassNumber { get { return this._ClassNumber; } }
		/// <summary>
		/// Vrstva grafu
		/// </summary>
		public GraphElementLayerType LayerType { get { return this._LayerType; } }
		/// <summary>
		/// Typ grafiky elementu
		/// </summary>
		public GraphElementShapeType ElementShapeType { get { return this._ElementShapeType; } }
		private RowGraphMode _GraphMode;
		private int _ClassNumber;
		private GraphElementLayerType _LayerType;
		private GraphElementShapeType _ElementShapeType;
		private int _GraphModeInt { get { return (int)this._GraphMode; } }
		private int _LayerTypeInt { get { return (int)this._LayerType; } }
		private int _ElementShapeTypeInt { get { return (int)this._ElementShapeType; } }
	}
	#endregion
	#region CLASS GraphElementPainterArgs : DATA PRO KRESLENÍ ELEMENTU
	/// <summary>
	/// GraphElementPainterArgs : data pro kreslení jednoho elementu
	/// </summary>
	public class GraphElementPainterArgs
	{
		#region KONSTRUKCE
		public GraphElementPainterArgs(System.Windows.Forms.PaintEventArgs paintArgs, 
			GraphElementPainterKey elementKey, GraphElementDataCls element, GraphElementState elementState, IDataRow rowItem,
			bool elementIsEdited, Rectangle elementArea, Rectangle rowGraphArea, Point lastPoint)
		{
			_PaintArgs = paintArgs;
			_ElementKey = elementKey;
			_GraphElement = element;
			_ElementState = elementState;
			_RowItem = rowItem;
			_ElementIsEdited = elementIsEdited;
			_ElementArea = elementArea;
			_RowGraphArea = rowGraphArea;
			DetailArea = new Rectangle();
			LastPoint = lastPoint;

			// Default values:
			BackGroundColor = Color.Empty;
			BackLineColor = Color.Empty;
			BackLineWidth = 0F;
			OuterLineColor = Color.Empty;
			OuterLineWidth = 3F;
			UtilizationColor = Color.Empty;
			BackSideMinElementWidth = 25;
			BackSidePenColor = Color.Empty;
			BackSidePenWidth = 1;
			TopLinePenColor = Color.Empty;
			TopLinePenWidth = 0F;
			CaptionColor = Color.Empty;
		}
		private System.Windows.Forms.PaintEventArgs _PaintArgs;
		private GraphElementPainterKey _ElementKey;
		private GraphElementDataCls _GraphElement;
		private GraphElementState _ElementState;
		private IDataRow _RowItem;
		private bool _ElementIsEdited;
		private Rectangle _ElementArea;
		private Rectangle _RowGraphArea;

		#endregion
		#region PROPERTY ZÁKLADNÍ
		/// <summary>
		/// Argument pro podporu kreslení
		/// </summary>
		public System.Windows.Forms.PaintEventArgs PaintArgs { get { return this._PaintArgs; } }
		/// <summary>
		/// Klíč elementu (typ grafu, vrstva, třída, typ elementu)
		/// </summary>
		public GraphElementPainterKey ElementKey  { get { return this._ElementKey; } }
		/// <summary>
		/// Data elementu
		/// </summary>
		public GraphElementDataCls GraphElement { get { return this._GraphElement; } }
		/// <summary>
		/// Stav elementu.
		/// </summary>
		public GraphElementState ElementState { get { return this._ElementState; } }
		/// <summary>
		/// Data řádku, v němž element bydlí
		/// </summary>
		public IDataRow RowItem { get { return this._RowItem; } }
		/// <summary>
		/// Příznak, zda element je právě nyní editován
		/// </summary>
		public bool ElementIsEdited { get { return this._ElementIsEdited; } }
		/// <summary>
		/// Prostor vyhrazený pro element
		/// </summary>
		public Rectangle ElementArea { get { return this._ElementArea; } }
		/// <summary>
		/// Prostor řádku
		/// </summary>
		public Rectangle RowGraphArea { get { return this._RowGraphArea; } }
		/// <summary>
		/// Out parametr: prostor definující souřadnice štětce (Brush).
		/// Pokud bude třeba vytvořit defaultní štětec, bude vytvořen pro tento prostor.
		/// </summary>
		public Rectangle BrushArea;
		/// <summary>
		/// Out parametr: prostor elementu, který je vizuálně využitý.
		/// Tento prostor je aktivní (reaguje na myší akce).
		/// </summary>
		public Rectangle DetailArea;
		/// <summary>
		/// Ref parametr: poslední pozice. Pro některé typy grafů má význam, když se vykresluje spojitá čára nad elementy.
		/// Například Gantt s využitím kapacit, nebo skladová zásoba, atd.
		/// </summary>
		public Point LastPoint;
		#endregion
		#region PROPERTY DOTÁHNUTÉ Z PODŘÍZENÝCH DAT
		/// <summary>
		/// Typ grafu v aktuálním řádku
		/// </summary>
		public RowGraphMode GraphMode { get { return this.ElementKey.GraphMode; } }
		/// <summary>
		/// Číslo třídy elementu
		/// </summary>
		public int ClassNumber { get { return this.ElementKey.ClassNumber; } }
		/// <summary>
		/// Vrstva grafu
		/// </summary>
		public GraphElementLayerType LayerType { get { return this.ElementKey.LayerType; } }
		/// <summary>
		/// Typ grafiky elementu
		/// </summary>
		public GraphElementShapeType ElementShapeType { get { return this.ElementKey.ElementShapeType; } }
		/// <summary>
		/// Data grafického elementu
		/// </summary>
		public IDataElement DataElement { get { return this._GraphElement.DataElement; } }
		/// <summary>
		/// Barva pozadí grafického elementu
		/// </summary>
		public Color ElementBackColor { get { return this._GraphElement.DataElement.BackColor; } }
		#endregion
		#region PROPERTY GRAFICKÉ = POŽADAVKY A DATA PRO DALŠÍ KRESLENÍ

		/// <summary>Příznak, že painter zajistil vše, a nic defaultního se nebude kreslit</summary>
		public bool IsAllFinished { get; set; }

		/// <summary>Příznak, že painter definoval Rectangle BackGroundRectangle anebo polygon Point[] BackGroundPolygon, a je třeba jej naplnit barvou BrushFillBack</summary>
		public bool BackGroundRunSystem { get; set; }
		/// <summary>Rectangle představující pozadí, který se má vyplnit barvou BrushFillBack. Použije se, pokud není definován BackGroundPolygon, a pokud tento BackGroundRectangle není Empty. Jinak se použije this.DefaultDetailAreaBack</summary>
		public Rectangle BackGroundRectangle { get; set; }
		/// <summary>Polygon představující pozadí, který se má vyplnit barvou BrushFillBack. Pokud není null, použije se prioritně.</summary>
		public Point[] BackGroundPolygon { get; set; }
		/// <summary>Štětec, který je definovaný pro plnění Background. Pokud je null, pak systém použije výchozí.</summary>
		public Brush BackGroundBrush { get; set; }
		/// <summary>
		/// Barva štětce na pozadí, kterým se má vyplnit Background. 
		/// Použije se, pokud není definovaný štětec BackGroundBrush, a pokud tato barva není Empty (což je výchozí stav).
		/// Pokud nebude definovaný ani štětec BackGroundBrush, ani barva BackGroundColor, použije se pro vytvoření štětce systémová paleta.
		/// Systémovou paletou může být služba IGraphElementPalette, která pak bude generovat štětec ve své metodě GetBrush().
		/// Tento princip platí pro některé další položky.
		/// </summary>
		public Color BackGroundColor { get; set; }
		/// <summary>Požadavek na Dispose nástroje BackGroundBrush po jeho použití (pokud prvek pochází z palety, nesmí se nastavit na true !)</summary>
		public bool BackGroundBrushDispose { get; set; }

		/// <summary>Příznak, že painter definoval Rectangle BackGroundRectangle anebo polygon Point[] BackGroundPolygon, a je třeba jej orámovat perem BackLinePen</summary>
		public bool BackLineRunSystem { get; set; }
		/// <summary>Pero, kterým se má orámovat Background. Pokud je null, pak systém použije výchozí.</summary>
		public Pen BackLinePen { get; set; }
		/// <summary>Barva pera, kterým se má orámovat Background. Použije se, pokud není definované pero, a pokud tato barva není Empty (což je výchozí stav).</summary>
		public Color BackLineColor { get; set; }
		/// <summary>Šířka pera, kterým se má orámovat Background. Použije se, pokud není definované pero, a pokud tato šířka je větší než 0 (což je výchozí stav).</summary>
		public float BackLineWidth { get; set; }
		/// <summary>Požadavek na Dispose pera PenLineBack (protože bylo vytvořeno jako new jen pro tuto jednu operaci). Pokud je Pen přečten z palety, nesmí se nastavovat na true.</summary>
		public bool BackLinePenDispose { get; set; }

		/// <summary>Požadavek na spodní orámování = pod výplní a pod BackLine. Využívá Rectangle BackGroundRectangle anebo polygon Point[] BackGroundPolygon. Bude se kreslit POD všechny vrstvy.</summary>
		public bool OuterLineRunSystem { get; set; }
		/// <summary>Pero, kterým se má orámovat Background. Pokud je null, pak systém použije výchozí.</summary>
		public Pen OuterLinePen { get; set; }
		/// <summary>Barva pera, kterým se má orámovat Background. Použije se, pokud není definované pero, a pokud tato barva není Empty (což je výchozí stav).</summary>
		public Color OuterLineColor { get; set; }
		/// <summary>Šířka pera, kterým se má orámovat Background. Typicky má být větší než BackLineWidth, aby bylo vidět.</summary>
		public float OuterLineWidth { get; set; }
		/// <summary>Požadavek na Dispose pera OuterLinePen (protože bylo vytvořeno jako new jen pro tuto jednu operaci). Pokud je Pen přečten z palety, nesmí se nastavovat na true.</summary>
		public bool OuterLinePenDispose { get; set; }

		/// <summary>Příznak, že painter definoval Rectangle BackGroundRectangle, a chce jej orámovat vlevo a vpravo perem PenLineBack</summary>
		public bool BackSideRunSystem { get; set; }
		/// <summary>Minimální šířka elementu, pro kterou jsou vykreslovány boční linky.</summary>
		public int BackSideMinElementWidth { get; set; }
		/// <summary>Šířka pera pro boční linky.</summary>
		public Color BackSidePenColor { get; set; }
		/// <summary>Šířka pera pro boční linky.</summary>
		public float BackSidePenWidth { get; set; }

		/// <summary>Příznak, že painter definoval Rectangle FillUtilR anebo polygon Point[] FillUtilP, a je třeba jej naplnit barvou BrushFillUtil</summary>
		public bool UtilizationRunSystem { get; set; }
		/// <summary>Rectangle představující utilization, který se má vyplnit barvou BrushFillUtil</summary>
		public Rectangle UtilizationRectangle { get; set; }
		/// <summary>Polygon představující utilization, který se má vyplnit barvou BrushFillUtil</summary>
		public Point[] UtilizationPolygon { get; set; }
		/// <summary>Štětec, který je definovaný pro plnění Utilization. Pokud je null, pak systém použije výchozí.</summary>
		public Brush UtilizationBrush { get; set; }
		/// <summary>Barva štětce, kterým se má vyplnit Utilization. POZOR: barva má reagovat na aktuální hodnotu využití kapacit args.GraphElement.UtilizationRatio. Použije se, pokud není definovaný štětec, a pokud tato barva není Empty (což je výchozí stav).</summary>
		public Color UtilizationColor { get; set; }
		/// <summary>Požadavek na Dispose prvku UtilizationBrush po jeho použití (pokud prvek pochází z palety, nesmí se nastavit na true !)</summary>
		public bool UtilizationBrushDispose { get; set; }

		/// <summary>Příznak, že painter definoval sadu interních rectangle a barev pro vykreslení výplně</summary>
		public bool InnerShapeRunSystem { get; set; }
		/// <summary>Souhrn inner tvarů</summary>
		public List<InnerShapeCls> InnerShapeList
		{
			get
			{
				if (this._InnerShapeList == null)
				{
					this._InnerShapeList = new List<InnerShapeCls>();
					this.InnerShapeRunSystem = true;
				}
				return this._InnerShapeList;
			}
			set
			{
				this._InnerShapeList = value;
			}
		}
		private List<InnerShapeCls> _InnerShapeList;

		/// <summary>Příznak, že painter definoval polygon Point[] TopLinePolygon, a je třeba jej vykreslit perem TopLinePen nebo barvou TopLinePenColor</summary>
		public bool TopLineRunSystem { get; set; }
		/// <summary>Suoupis bodů, které se mají vykreslit jako TopLine</summary>
		public Point[] TopLinePolygon { get; set; }
		/// <summary>Pero, kterým se má orámovat Utilization. Pokud je null, pak systém použije výchozí.</summary>
		public Pen TopLinePen { get; set; }
		/// <summary>Šířka pera pro horní linku.</summary>
		public Color TopLinePenColor { get; set; }
		/// <summary>Šířka pera pro horní linku.</summary>
		public float TopLinePenWidth { get; set; }
		/// <summary>Požadavek na Dispose prvku TopLinePen po jeho použití (pokud prvek pochází z palety, nesmí se nastavit na true !)</summary>
		public bool TopLinePenDispose { get; set; }

		/// <summary>Příznak, že painter vyžaduje vykreslení Caption do elementu</summary>
		public bool CaptionRunSystem { get; set; }
		/// <summary>Font pro kreslení popisku</summary>
		public Font CaptionFont { get; set; }
		/// <summary>Explicitní text popisku</summary>
		public string CaptionText { get; set; }
		/// <summary>Požadavek na Dispose nástroje CaptionFont po jeho použití (pokud prvek pochází z palety, nesmí se nastavit na true !)</summary>
		public bool CaptionFontDispose { get; set; }
		/// <summary>Barva pro kreslení popisku</summary>
		public Brush CaptionBrush { get; set; }
		/// <summary>Barva štětce, kterým se má psát text. Použije se, pokud není definovaný štětec, a pokud tato barva není Empty (což je výchozí stav).</summary>
		public Color CaptionColor { get; set; }
		/// <summary>Požadavek na Dispose prvku CaptionBrush po jeho použití (pokud prvek pochází z palety, nesmí se nastavit na true !)</summary>
		public bool CaptionBrushDispose { get; set; }
		/// <summary>Prostor pro kreslení Caption. Lze sem uložit this.DefaultCaptionArea, ale to zvládne defaultní painter taky.</summary>
		public Rectangle CaptionArea { get; set; } 

		#endregion
		#region PROPERTY ROZŠÍŘENÉ
		/// <summary>
		/// Aktuální platná vizuální pozice položky grafu.
		/// Souřadnice jsou uvedeny v koordinátech Controlu, v němž je element vykreslen.
		/// Prostor je oříznutý do rozměrů aktuálního grafu (na rozdíl od ElementArea, který definuje pozici elementu v logickém prostoru grafu).
		/// Tento údaj se aktualizuje při každém vykreslení grafu.
		/// </summary>
		public Rectangle VisualRectangle { get { return this.GraphElement.VisualRectangle; } set { this.GraphElement.VisualRectangle = value; } }
		/// <summary>
		/// Defaultní souřadnice pro VisualRectangle ( = Rectangle.Intersect(this.DetailArea, this.RowGraphArea) )
		/// </summary>
		public Rectangle DefaultVisualRectangle { get { return Rectangle.Intersect(this.DetailArea, this.RowGraphArea); } }
		/// <summary>
		/// Implicitní prostor pro DetailAreaBack (pozadí) = prostor 
		/// ve fyzické souřadnici this.ElementArea(X,Y) + offset this.GraphElement.RectangleBack.Y (tzn. absolutní pozice prostoru grafu v řádku v controlu)
		/// ve velikosti this.GraphElement.RectangleBack (tzn. celá prostor elementu)
		/// </summary>
		public Rectangle DefaultDetailAreaBack
		{
			get
			{
				return new Rectangle(
					this.ElementArea.X, this.ElementArea.Y + this.GraphElement.RectangleBack.Y,
					this.GraphElement.RectangleBack.Width, this.GraphElement.RectangleBack.Height);
			}
		}
		/// <summary>
		/// Implicitní prostor pro DetailAreaElement (významová část grafu) = prostor 
		/// ve fyzické souřadnici this.ElementArea(X,Y) + offset this.GraphElement.ElementArea.Y (tzn. absolutní pozice prostoru grafu v řádku v controlu)
		/// ve velikosti this.GraphElement.ElementArea (tzn. vnitřní rozměr elementu)
		/// </summary>
		public Rectangle DefaultDetailAreaElement
		{
			get
			{
				Rectangle result = new Rectangle(
					this.ElementArea.X, this.ElementArea.Y + this.GraphElement.ElementArea.Y,
					this.GraphElement.ElementArea.Width, this.GraphElement.ElementArea.Height);
				return result;
			}
		}
		/// <summary>
		/// Implicitní prostor pro Caption v elementu = prostor 
		/// ve v průsečíku (this.DetailArea, this.RowGraphArea), v případě potřeby zvětšený na výšku 14px
		/// </summary>
		public Rectangle DefaultCaptionArea
		{
			get
			{
				Rectangle captionArea = Rectangle.Intersect(this.DetailArea, this.RowGraphArea);
				captionArea.Y += 1;
				if (captionArea.Height < 14)
				{
					int vc = captionArea.Y + captionArea.Height / 2;
					captionArea.Y = vc - 7;
					captionArea.Height = 14;
				}
				return captionArea;
			}
		}
		/// <summary>
		/// Vrátí klíč palety pro tento prvek, pro danou barvu
		/// </summary>
		/// <param name="color"></param>
		/// <returns></returns>
		public GraphElementPaletteItem GetPaletteItemForColor(Color color)
		{
			return new GraphElementPaletteItem(this.GraphMode, this.ElementShapeType, color, this.ElementState);
		}
		#endregion
		#region PŘÍSTUP K METODÁM OBJEKTŮ
		/// <summary>
		/// Metoda vrátí optimální text prodaný počet znaků.
		/// Ten je závislý na fontu a velikosti elementu.
		/// Počet znaků musí určit painter podle aktuální velikosti a fontu. Element může jen najít vhodný text v potřebné délce.
		/// </summary>
		/// <param name="charCount"></param>
		/// <returns></returns>
		public string GetElementCaption(float charCount)
		{
			// Voláme aplikační metodu, která určí text:
			return this.GraphElement.DataElement.Caption(this.RowItem, charCount);
		}
		#endregion
		#region SUBCLASS InnerShapes : vnitřní obrazce s jednoduchou barvou
		/// <summary>
		/// Třída, která definuje vnitřní tvar uvnitř elementu.
		/// </summary>
		public class InnerShapeCls
		{
			public InnerShapeCls()
			{
				this.InnerRectangle = Rectangle.Empty;
				this.InnerBackColor = Color.Empty;
				this.InnerLineColor = Color.Empty;
			}
			public InnerShapeCls(Rectangle innerRectangle, Color innerBackColor)
			{
				this.InnerRectangle = innerRectangle;
				this.InnerBackColor = innerBackColor;
				this.InnerLineColor = Color.Empty;
			}
			public InnerShapeCls(Rectangle innerRectangle, Color innerBackColor, Color innerLineColor)
			{
				this.InnerRectangle = innerRectangle;
				this.InnerBackColor = innerBackColor;
				this.InnerLineColor = innerLineColor;
			}
			public InnerShapeCls(Point[] innerPolygon, Color innerBackColor)
			{
				this.InnerRectangle = Rectangle.Empty;
				this.InnerPolygon = innerPolygon;
				this.InnerBackColor = innerBackColor;
				this.InnerLineColor = Color.Empty;
			}
			public InnerShapeCls(Point[] innerPolygon, Color innerBackColor, Color innerLineColor)
			{
				this.InnerRectangle = Rectangle.Empty;
				this.InnerPolygon = innerPolygon;
				this.InnerBackColor = innerBackColor;
				this.InnerLineColor = innerLineColor;
			}
			/// <summary>Vnitřní prostor ve tvaru obdélníku - jeho tvar</summary>
			public Rectangle InnerRectangle { get; set; }
			/// <summary>Vnitřní prostor ve tvaru polygonu - jeho tvar</summary>
			public Point[] InnerPolygon { get; set; }
			/// <summary>Vnitřní prostor - jeho barva pozadí, Empty = nebude vykresleno</summary>
			public Color InnerBackColor { get; set; }
			/// <summary>Vnitřní prostor - jeho barva orámování, Empty = nebude vykresleno</summary>
			public Color InnerLineColor { get; set; }
			/// <summary>Popisek vnitřního textu</summary>
			public string InnerCaption { get; set; }
		}
		#endregion
	}
	#endregion
	#region CLASS GraphElementDataCls : data pro jednotlivou položku grafu v řádku
	/// <summary>
	/// GraphElementDataCls : data pro jednotlivou položku grafu v řádku.
	/// Obsahuje jak data získaná z datového zdroje, tak data dopočítaná pro aktuální zobrazení.
	/// </summary>
	public class GraphElementDataCls
	{
		#region KONSTRUKCE
		/// <summary>
		/// Konstruktor
		/// </summary>
		/// <param name="dataElement"></param>
		public GraphElementDataCls(IDataElement dataElement)
		{
			this.DataElement = dataElement;
		}
		/// <summary>
		/// Logická data o grafickém elementu.
		/// Jde o data, která vrátila datová vrstva.
		/// Obsahují zejména časový interval, typ údaje, specifikaci grafu, režim pohybu a další.
		/// </summary>
		public IDataElement DataElement;
		/// <summary>
		/// Specifikuje druh obsahu, ovlivňuje způsob zobrazování (barvu, vzhled).
		/// Datová vrstva zde může odlišovat různé typy směn, různé režimy práce, různé úrovně hierarchie zakázky, různé zobrazované třídy.
		/// U grafu typu RowGraphMode.Gantt ovlivní barvu políčka, vyjadřuje druh činnosti.
		/// U grafu typu RowGraphMode.CapacityUtilization ovlivní barvu políčka, vyjadřuje druh směny.
		/// U grafu typu RowGraphMode.TaskCapacityLink ovlivňuje barvu políčka směny (pokud je this.ElementLayer == SubLayer), nebo práce (pokud je this.ElementLayer == ItemLayer).
		/// Konkrétní barvu generuje metoda IDataSource.GetColorForElement()
		/// </summary>
		public GraphElementVisualType ElementVisualType { get { return this.DataElement.ElementVisualType; } }
		/// <summary>
		/// Tvar elementu.
		/// </summary>
		public GraphElementShapeType ElementShapeType { get { return this.DataElement.ElementShapeType; } }
		/// <summary>
		/// Využití stavu kapacity v poměrné hodnotě (1.00 = 100%). Záporné hodnoty mají význam jako 0. 
		/// Hodnoty nad 1.00 značí přečerpání kapacit a zobrazují se.
		/// </summary>
		public float UtilizationRatio { get { return this.DataElement.UtilizationRatio;	} }
		/// <summary>
		/// Pozice elementu grafu v rámci aktuálního grafu řádku.
		/// Souřadnice X je relativní k aktuálnímu počátku grafu podle aktuálního počátku časové osy. Odpovídá času Begin. Může být záporná.
		/// Dimenze Width odpovídá době trvání elementu.
		/// Souřadnice Y je 1.
		/// Dimenze Height je dána výškou řádku - 2 (řeší se pouze GridLines).
		/// Tento údaj se aktualizuje pouze při posunech logických dat a při posunech časové osy a jejího měřítka.
		/// Při scrollování grafu nahoru/dolů se nemění.
		/// Při posouvání času doleva/doprava nebo změně časového měřítka se objekty přepočítávají.
		/// </summary>
		public Rectangle ElementArea;
		/// <summary>
		/// Pozice detailu grafu = může být podmnožinou prostoru ElementArea.
		/// Typicky se využívá v grafu typu TaskCapacityLink pro zobrazení elementů typu TaskLink, v rámci konkrétní linky,
		/// viz příprava v metodě this._ChartElementsPrepareVisual().
		/// </summary>
		public Rectangle DetailArea;
		/// <summary>
		/// Číslo linky, ve které se zobrazuje tento element. Týká se pouze grafů typu TaskCapacityLink.
		/// Linky začínají hodnotou 1. Linka s číslem 0 je vyhrazena pro přesouvání, a existuje vždy.
		/// </summary>
		public int PipeLine;
		/// <summary>
		/// GID elementu, který obsahuje sloučenou WorkEnergy z tohoto elementu.
		/// Při slučování elementů na časové ose (při vykreslování TaskLink) se z více kontinuálních elementů stává jeden element (časově první),
		/// který do sebe akumuluje energii z návazných elementů. Návazné elementy se nulují (mají Empty čas a nulovou energii).
		/// V této proměnné (PrevElementGIdInPipe) se ve vyprázdněných elementech uchovává GID elementu, který na sebe přebírá energii z tohoto elementu.
		/// </summary>
		public GID PrevElementGIdInPipe;
		/// <summary>
		/// Zobrazovaná pozice sub položky grafu Back (vykreslované pozadí). 
		/// Data jsou plněna jen pro určité typy grafů, viz metoda this.PrepareVisualDataForGraph() a návazné.
		/// Platné údaje jsou X, Width, Y, Height. 
		/// Pozice X a Y jsou relativní k pozici X,Y buňky, v níž se graf vykresluje.
		/// Rozměry Width a Height jsou v pixelech.
		/// Tento údaj se aktualizuje pouze při posunech logických dat a při posunech časové osy a jejího měřítka.
		/// Při scrollování grafu se nemění.
		/// </summary>
		public Rectangle RectangleBack;
		/// <summary>
		/// Vrchol použité kapacity, nejvyšší souřadnice Y
		/// </summary>
		public int CapacityUsedTop;
		/// <summary>
		/// Aktuální platná vizuální pozice položky grafu.
		/// Souřadnice jsou uvedeny v koordinátech Controlu, v němž je element vykreslen.
		/// Prostor je oříznutý do rozměrů aktuálního grafu (na rozdíl od ElementArea, který definuje pozici elementu v logickém prostoru grafu).
		/// Tento údaj se aktualizuje při každém vykreslení grafu.
		/// </summary>
		public Rectangle VisualRectangle;
		/// <summary>
		/// Příznak, že tento prvek grafu je nyní vhodné zobrazit.
		/// </summary>
		public bool Visible;
		/// <summary>
		/// Příznak, že element se právě nyní nachází v editoru. Pak se s ním zachází jinak při výpočtech pro zobrazení.
		/// </summary>
		public bool IsInEditingState { get { return _IsInEditingState; } }
		protected bool _IsInEditingState;
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{GraphElement: X=" + this.ElementArea.X.ToString() + "; Width=" + this.ElementArea.Width.ToString() + "; Data: " + this.DataElement.ToString() + "}";
		}
		/// <summary>
		/// Komparátor podle vizuálních priorit.
		/// Nejprve dává objekty "dospodu", a v rámci vrstvy je pak třídění podle času Begin
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static int CompareByVisualLayer(GraphElementDataCls a, GraphElementDataCls b)
		{
			int cl = ((int)a.DataElement.ElementLayer).CompareTo((int)b.DataElement.ElementLayer);
			if (cl != 0) return cl;

			int cb = a.DataElement.TimeRange.Begin.CompareTo(b.DataElement.TimeRange.Begin);
			if (cb != 0) return cb;

			int cp = a.DataElement.ParentWorkGId.CompareTo(b.DataElement.ParentWorkGId);
			if (cp != 0) return cp;

			int cw = a.DataElement.WorkGId.CompareTo(b.DataElement.WorkGId);
			if (cw != 0) return cw;

			int ce = a.DataElement.GId.CompareTo(b.DataElement.GId);
			return ce;
		}
		/// <summary>
		/// Komparátor podle času počátku. Neřeší vrstvu ElementLayer.
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static int CompareByTimeRangeBegin(GraphElementDataCls a, GraphElementDataCls b)
		{
			if (a.DataElement.ElementLayer != b.DataElement.ElementLayer)
				// Objekty na různých vrstvách: nižší vrstva bude dříve:
				return ((int)a.DataElement.ElementLayer).CompareTo((int)b.DataElement.ElementLayer);
			// Objekty na stejné vrstvě: objekt s nižším Begin bude dříve:
			return a.DataElement.TimeRange.Begin.CompareTo(b.DataElement.TimeRange.Begin);
		}
		/// <summary>
		/// Komparátor podle vizuálních priorit.
		/// Nejprve dává objekty "dospodu", a v rámci vrstvy je pak třídění podle času Begin
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static int CompareByWorkGIdBegin(GraphElementDataCls a, GraphElementDataCls b)
		{
			if (a.DataElement.WorkGId != b.DataElement.WorkGId)
				// Objekty z různých WorkGID: 
				return (a.DataElement.WorkGId.CompareTo(b.DataElement.WorkGId));
			// Objekty ze stejného WorkGID: objekt s nižším Begin bude dříve:
			return a.DataElement.TimeRange.Begin.CompareTo(b.DataElement.TimeRange.Begin);
		}
		#endregion
		#region PROPERTY IsXxxx : zjistí, zda element má určitou vlastnost, na základě jeho hodnoty DataElement.ElementEditingMode
		/// <summary>
		/// { get; } Obsahuje true, pokud objekt lze přesouvat na jiný čas = změnit jeho čas Begin a End (Movable).
		/// Pokud objekt není Movable, pak s ním nic nepohne, ani změny okolních objektů.
		/// </summary>
		public bool IsMovable { get { return (MoveModeIsExact(this.DataElement.ElementEditingMode, DataElementEditingMode.Movable)); } }
		/// <summary>
		/// { get; } Obsahuje true, pokud objekt lze resizovat = změnit jeho šířku (Resizable)
		/// </summary>
		public bool IsResizable { get { return (MoveModeIsExact(this.DataElement.ElementEditingMode, DataElementEditingMode.Resizable)); } }
		/// <summary>
		/// Příznak, že tento element je fixní, není možno jej editovat (ani přesouvat, ani resizovat).
		/// </summary>
		public bool IsFixed { get { return ((this.DataElement.ElementEditingMode & DataElementEditingMode.Editable) == DataElementEditingMode.FixedItem); } }
		/// <summary>
		/// { get; } Obsahuje true, pokud objekt je aktivní na pohyb myši, lze jej selectovat, reaguje. false = objekt se chová jako mrtvý brouk.
		/// </summary>
		public bool IsSelectable { get { return (this.DataElement.ElementLayer == GraphElementLayerType.ItemLayer && MoveModeIsAny(this.DataElement.ElementEditingMode, DataElementEditingMode.Movable | DataElementEditingMode.Resizable)); } }
		/// <summary>
		/// { get; } Obsahuje true, pokud objekt lze manuálně přesouvat nebo resizovat = lze jej aktivně měnit (Movable or Resizable)
		/// </summary>
		public bool IsEditable { get { return (this.DataElement.ElementLayer == GraphElementLayerType.ItemLayer && MoveModeIsAny(this.DataElement.ElementEditingMode, DataElementEditingMode.Movable | DataElementEditingMode.Resizable)); } }
		/// <summary>
		/// { get; } Obsahuje true, pokud objekt je pevně spojen se sousedními objekty (FixedLinkItemsOnSameThread)
		/// </summary>
		public bool IsJoinedToAdjacent { get { return (MoveModeIsExact(this.DataElement.ElementEditingMode, DataElementEditingMode.FixedLinkItemsOnSameThread)); } }
		/// <summary>
		/// { get; } Obsahuje true, pokud objekt je relativně svázán se sousedními objekty (ShiftItemsOnSameThread)
		/// </summary>
		public bool IsRelativeToAdjacent { get { return (MoveModeIsExact(this.DataElement.ElementEditingMode, DataElementEditingMode.ShiftItemsOnSameThread)); } }
		/// <summary>
		/// { get; } Obsahuje true, pokud objekt se při editaci snaží vracet na původní čas, pokud se tam volné místo.
		/// </summary>
		public bool IsAffinedToInitialTime { get { return (MoveModeIsExact(this.DataElement.ElementEditingMode, DataElementEditingMode.AffinityToInitialTime)); } }
		/// <summary>
		/// { get; } Obsahuje true, pokud objekt lze posouvat jen v rámci času daného parent objektem (MoveOnlyInParent)
		/// </summary>
		public bool IsLimitedToParent { get { return (MoveModeIsExact(this.DataElement.ElementEditingMode, DataElementEditingMode.MoveOnlyInParent)); } }
		/// <summary>
		/// { get; } Obsahuje true, pokud element lze editovat jen v rámci rastru (pracovní doba)
		/// </summary>
		public bool IsEditableViaGrid { get { return (this.IsSelectable && MoveModeIsAny(this.DataElement.ElementEditingMode, DataElementEditingMode.EditingViaGrid)); } }
		/// <summary>
		/// { get; } Obsahuje true, pokud pohyb není omezen na prostor parenta, ale po pohybu tohoto objektu má být časový rozsah parenta rozšířen tak, aby zahrnoval i nový čas tohoto elementu (MoveAnywhereWithParentResize)
		/// </summary>
		public bool NeedParentResize { get { return (MoveModeIsExact(this.DataElement.ElementEditingMode, DataElementEditingMode.MoveAnywhereWithParentResize)); } }
		/// <summary>
		/// { get; } Obsahuje true, pokud tento objekt je prázdný. Svou prázdnotu odvozuje od this.DataElement.GId.IsEmpty.
		/// </summary>
		public bool IsEmpty { get { return this.DataElement.GId.IsEmpty; } }
		#endregion
		#region PODPORA DETEKCE STAVŮ TYPU DataElementEditingMode
		/// <summary>
		/// Jednoduchá detekční metoda pro Enum typu DataElementEditingMode [Flag]: zjistí, zda v hodnotě (editingMode)
		/// je nahozen bit(y) odpovídající hodnotě detectState. Vrací true, pokud v dané hodnotě je aktivní daný stav.
		/// Hodnota detectState je typicky základní, jednobitová.
		/// Hodnota detectState může být i vícebitová, pak funkce vrátí true jen pokud v dané hodnotě jsou nastaveny všechny bity.
		/// Pokud je jako detectState předána 0 (None), pak funkce vrací vždy true.
		/// </summary>
		/// <param name="editingMode">Typicky element.ElementEditingMode</param>
		/// <param name="detectState">Testovaný bit (bity).</param>
		/// <returns>true = testovaný bit je aktivní, je nahozen na 1 / false = neaktivní</returns>
		protected static bool MoveModeIsExact(DataElementEditingMode editingMode, DataElementEditingMode detectState)
		{
			return ((editingMode & detectState) == detectState);
		}
		/// <summary>
		/// Jednoduchá detekční metoda pro Enum typu DataElementEditingMode [Flag]: zjistí, zda v hodnotě (editingMode)
		/// je nahozen některý bit(y) odpovídající hodnotě detectState. Vrací true, pokud v dané hodnotě je aktivní alespoň jeden bit z daného stavu.
		/// Hodnota detectState je typicky kombinovaná, vícebitová.
		/// Hodnota detectState může být i jednobitová, pak funkce vrátí totéž jako funkce _ModeIsExact().
		/// Pokud je jako detectState předána 0 (None), pak funkce vrací vždy false.
		/// </summary>
		/// <param name="editingMode">Typicky element.ElementEditingMode</param>
		/// <param name="detectState">Testované bity (bit).</param>
		/// <returns>true = hodnota obsahuje přinejmenším jeden z testovaných bitů / false = ani jeden</returns>
		protected static bool MoveModeIsAny(DataElementEditingMode editingMode, DataElementEditingMode detectState)
		{
			return ((editingMode & detectState) > 0);
		}
		#endregion
	}
	#endregion
}
